package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import utils.Configuration;

import network.config.NetworkConfiguration;
import network.config.NetworkConfiguration.PortType;

/**
 * UDPListener is a simple Thread class that listen for
 * incoming UDP packet and redirect all of them to
 * a network interface.
 */
public class UDPListener extends Thread {
	
	private DatagramSocket socket ;
	private boolean running ;
	private AbstractNetworkInterface networkInterface ;
	private NetworkConfiguration networkConfig ;
	
	private static final int UDP_BUFFER_SIZE = 2048 ;
	private static final int UDP_RECEIVER_TIMEOUT = 50 ; // in milliseconds
	
	/**
	 * Create a new UDPListener that will listen on the port specified
	 * by the netconf parameter and give all receive packet to
	 * the AbstractNetworkInterface.
	 * 
	 * The listener is started by calling the start method. It must be
	 * restarted if the network configuration change.
	 * To close the listener properly, use the finish method.
	 * 
	 * @param netconf The network configuration
	 * @param abstractNetworkInterface The interface that will receive signals
	 * @throws SocketException if an error occurs creating the UDP socket
	 */
	public UDPListener (NetworkConfiguration netconf, AbstractNetworkInterface abstractNetworkInterface) throws SocketException {
		this.networkConfig = netconf ;
		this.socket = new DatagramSocket (this.networkConfig.getPort(PortType.UDP_RECEIVE)) ;
		this.socket.setSoTimeout(UDP_RECEIVER_TIMEOUT) ;
		this.running = true ;
		this.networkInterface = abstractNetworkInterface ;
	}
	
	/**
	 * Stop the listener thread properly.
	 */
	public void finish () {
		this.running = false ;
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	public void run () {
		DatagramPacket packet = new DatagramPacket (new byte[UDP_BUFFER_SIZE], UDP_BUFFER_SIZE) ;
		while (this.running) {
			try {
				this.socket.receive(packet);
				if (this.networkConfig.isAllowedAddress(packet.getAddress())) {
					Configuration.net_debug.println("Receive from : " + packet.getSocketAddress() + ", accept.") ;
					this.networkInterface.addSignal(packet) ;
					synchronized (this.networkInterface) {
						this.networkInterface.notify() ;
					}
				}
				else {
					Configuration.net_debug.println("Receive from : " + packet.getSocketAddress() + ", drop.") ;
				}
			} 
			catch (SocketTimeoutException e) {
				
			}
			catch (IOException e) {	
			} 			
		}
		this.socket.close() ;
	}
	
}
